/**
  ******************************************************************************
  * @file    sym32l010_hal_sysctrl.c
  * @author  AE Team
  * @version 1.0.3
  * @date    2024-05-28
  * @brief   SYSCTRL HAL module driver.
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 SIYIMicro.
  * All rights reserved.</center></h2>
  *
  *
  ******************************************************************************
  */


/******************************************************************************/
/* Include files                                                              */
/******************************************************************************/
#include "sym32l010_hal_sysctrl.h"


/******************************************************************************/
/* global static variables                                                    */
/******************************************************************************/
static uint32_t gHseFreq = 0x00;
static uint32_t gLseFreq = 0x00;

/******************************************************************************/
/* Local function prototypes ('static')                                       */
/******************************************************************************/
static uint16_t SysCtrl_GetHSITrimCode(SYSCTRL_HsiOscDivTypeDef HsiDiv);


/*******************************************************************************
  * @brief  更改系统时钟来源
  *
  * @param  NewClkSource: 目标时钟源
  *                       @arg SYSCTRL_SYSCLKSRC_HSI
  *                       @arg SYSCTRL_SYSCLKSRC_HSE
  *                       @arg SYSCTRL_SYSCLKSRC_LSI
  *                       @arg SYSCTRL_SYSCLKSRC_LSE
  *
  * @retval 切换后的系统时钟来源
  *
  * @note   调整系统时钟频率时需同步调整FLASH的访问速率(SYSCTRL_CR2.FLASHWAIT / FLASH_CR2.WAIT)
  *
  */
uint8_t HAL_SysCtrl_SysClk_Switch(SYSCTRL_SysClkSrcTypeDef NewClkSource)
{
    if (SYM_SYSCTRL->CR1 & (0x1UL << (uint32_t)NewClkSource))         // 时钟有效才进行切换
    {
        REGBITS_MODIFY(SYM_SYSCTRL->CR0, SYSCTRL_CR0_SYSCLK_Msk, (SYSCTRL_CR_KEY | (uint32_t)NewClkSource));
    }

    SystemCoreClock = HAL_SysCtrl_GetHclkFreq();                      // 更新内核时钟频率

    return (SYM_SYSCTRL->CR0 & SYSCTRL_CR0_SYSCLK_Msk);
}


/*******************************************************************************
  * @brief  配置 SYSCLK 与 HCLK 的分频比
  * @param  Div: 分频系数
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV1
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV2
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV4
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV8
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV16
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV32
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV64
  *              @arg SYSCTRL_SYSCLK2HCLK_DIV128
  *
  * @retval None
  */
void HAL_SysCtrl_HCLK_SetDiv(SYSCTRL_HclkPrsTypeDef Div)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR0, SYSCTRL_CR0_HCLKPRS_Msk, (SYSCTRL_CR_KEY | (uint32_t)Div));

    SystemCoreClock = HAL_SysCtrl_GetHclkFreq();                      // 更新内核时钟频率
}


/*******************************************************************************
  * @brief  配置 HCLK 与 PCLK 的分频比
  * @param  Div: 分频系数
  *              @arg SYSCTRL_HCLK2PCLK_DIV1
  *              @arg SYSCTRL_HCLK2PCLK_DIV2
  *              @arg SYSCTRL_HCLK2PCLK_DIV4
  *              @arg SYSCTRL_HCLK2PCLK_DIV8
  *
  * @retval None
  */
void HAL_SysCtrl_PCLK_SetDiv(SYSCTRL_PclkPrsTypeDef Div)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR0, SYSCTRL_CR0_PCLKPRS_Msk, (SYSCTRL_CR_KEY | (uint32_t)Div));
}


/*******************************************************************************
  * @brief  使能内部高频振荡器，并配置 HSIOSC 与 HSI 的分频比
  * @param  HsiDiv: 分频系数
  *                 @arg HSIOSC_TO_HSI48MHZ
  *                 @arg HSIOSC_TO_HSI24MHZ
  *                 @arg HSIOSC_TO_HSI16MHZ
  *                 @arg HSIOSC_TO_HSI12MHZ
  *                 @arg HSIOSC_TO_HSI9P60MHZ
  *                 @arg HSIOSC_TO_HSI8MHZ
  *                 @arg HSIOSC_TO_HSI6P86MHZ
  *                 @arg HSIOSC_TO_HSI6MHZ
  *                 @arg HSIOSC_TO_HSI5P33MHZ
  *                 @arg HSIOSC_TO_HSI4P80MHZ
  *                 @arg HSIOSC_TO_HSI4P36MHZ
  *                 @arg HSIOSC_TO_HSI4MHZ
  *                 @arg HSIOSC_TO_HSI3P69MHZ
  *                 @arg HSIOSC_TO_HSI3P43MHZ
  *                 @arg HSIOSC_TO_HSI3P20MHZ
  *                 @arg HSIOSC_TO_HSI3MHZ
  * @retval None
  */
void HAL_SysCtrl_HSI_Enable(SYSCTRL_HsiOscDivTypeDef HsiDiv)
{
    SYM_SYSCTRL->HSI = (uint32_t)HsiDiv | SysCtrl_GetHSITrimCode(HsiDiv);
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY | SYSCTRL_CR1_HSIEN_Msk));      // Enable HSI
    while ((SYM_SYSCTRL->HSI & SYSCTRL_HSI_STABLE_Msk) != SYSCTRL_HSI_STABLE_Msk) // Wait stable
    {
        ;
    }

    SystemCoreClock = HAL_SysCtrl_GetHclkFreq();                                  // 更新内核时钟频率
}


/*******************************************************************************
  * @brief  配置 HSIOSC 与 HSI 的分频比
  * @param  HsiDiv: 分频系数
  *                 @arg HSIOSC_TO_HSI48MHZ
  *                 @arg HSIOSC_TO_HSI24MHZ
  *                 @arg HSIOSC_TO_HSI16MHZ
  *                 @arg HSIOSC_TO_HSI12MHZ
  *                 @arg HSIOSC_TO_HSI9P60MHZ
  *                 @arg HSIOSC_TO_HSI8MHZ
  *                 @arg HSIOSC_TO_HSI6P86MHZ
  *                 @arg HSIOSC_TO_HSI6MHZ
  *                 @arg HSIOSC_TO_HSI5P33MHZ
  *                 @arg HSIOSC_TO_HSI4P80MHZ
  *                 @arg HSIOSC_TO_HSI4P36MHZ
  *                 @arg HSIOSC_TO_HSI4MHZ
  *                 @arg HSIOSC_TO_HSI3P69MHZ
  *                 @arg HSIOSC_TO_HSI3P43MHZ
  *                 @arg HSIOSC_TO_HSI3P20MHZ
  *                 @arg HSIOSC_TO_HSI3MHZ
  * @retval None
  * @note   若HSI为系统时钟，需同步调整FLASH的访问速率(SYSCTRL_CR2.FLASHWAIT / FLASH_CR2.WAIT)
  */
void HAL_SysCtrl_HSI_SetDiv(SYSCTRL_HsiOscDivTypeDef HsiDiv)
{
    SYM_SYSCTRL->HSI = (uint32_t)HsiDiv | SysCtrl_GetHSITrimCode(HsiDiv);

    SystemCoreClock = HAL_SysCtrl_GetHclkFreq();                      // 更新内核时钟频率
}


/*******************************************************************************
  * @brief  禁止内部高频振荡器
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_HSI_Disable(void)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR1, SYSCTRL_CR1_HSIEN_Msk, SYSCTRL_CR_KEY);
}


/*******************************************************************************
  * @brief  使能内部低频振荡器
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_LSI_Enable(void)
{
    SYM_SYSCTRL->LSI = TRIMCODE_LSI_VALUE | SYSCTRL_LSI_WAITCYCLE_258;            // Load Trim Value
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY | SYSCTRL_CR1_LSIEN_Msk));      // Enable LSI
    while ((SYM_SYSCTRL->LSI & SYSCTRL_LSI_STABLE_Msk) != SYSCTRL_LSI_STABLE_Msk) // Wait stable
    {
        ;
    }
}


/*******************************************************************************
  * @brief  禁止内部低频振荡器
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_LSI_Disable(void)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR1, SYSCTRL_CR1_LSIEN_Msk, SYSCTRL_CR_KEY);
}


/*******************************************************************************
  * @brief  使能晶振振荡运行过程中失效时自动将SysCtrl的时钟源切换为HSI8M
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_CLKCCS_Enable(void)
{
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY | SYSCTRL_CR1_CLKCCS_Msk));
}


/*******************************************************************************
  * @brief  晶振振荡运行过程中失效时无动作
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_CLKCCS_Disable(void)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR1, SYSCTRL_CR1_CLKCCS_Msk, SYSCTRL_CR_KEY);
}


/*******************************************************************************
  * @brief  使能高频晶振(晶体振荡模式)
  * @param  FreqIn   : 晶振频率
  *                    4000000 - 32000000Hz
  * @param  Driver   : 驱动能力
  *                    @arg SYSCTRL_HSE_DRIVER_0
  *                    @arg SYSCTRL_HSE_DRIVER_1
  *                    @arg SYSCTRL_HSE_DRIVER_2
  *                    @arg SYSCTRL_HSE_DRIVER_3
  *                    @arg SYSCTRL_HSE_DRIVER_4
  *                    @arg SYSCTRL_HSE_DRIVER_5
  *                    @arg SYSCTRL_HSE_DRIVER_6
  *                    @arg SYSCTRL_HSE_DRIVER_7
  * @retval HAL_OK   : 起振成功
  *         HAL_ERROR: 起振失败
  * @note   注意：选择负载电容小的晶体更容易起振。
  *         注意：HSE使能时开启了HSECCS；如需减少功耗可关闭HSECCS
  */
HAL_StatusTypeDef HAL_SysCtrl_HSE_Enable_Crystal(uint32_t FreqIn, SYSCTRL_HseDrivrTypeDef Driver)
{
    if ((FreqIn < 4000000) || (FreqIn > 32000000))
    {
        while (1)
        {
            ;                                                                      // Error Freq
        }
    }

    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOA_Msk));   // Open GPIOA Clk
    REGBITS_SET(SYM_GPIOA->ANALOG, GPIOx_ANALOG_PIN0_Msk | GPIOx_ANALOG_PIN1_Msk); // PA00 PA01 as analog pin
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;
    uint32_t DetCnt  = (8000000 / (FreqIn / 1000)) << SYSCTRL_HSE_DETCNT_Pos;
    uint32_t PDriver = (Driver >= SYSCTRL_HSE_DRIVER_5) ? Driver : (Driver + 1);
    SYM_SYSCTRL->ICR = ~SYSCTRL_ICR_HSEFAIL_Msk;                                   // Clear HSE Fail Flag
    SYM_SYSCTRL->HSE = (uint32_t)SYSCTRL_HSE_MODE_OSC                 |
                       (uint32_t)SYSCTRL_HSE_WAITCYCLE_262144         |
                       DetCnt                                         | 
                       (uint32_t)(PDriver << SYSCTRL_HSE_PDRIVER_Pos) | 
                       (uint32_t)Driver;
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY         |
                                   SYSCTRL_CR1_HSECCS_Msk |                        // Enable HSECCS
                                   SYSCTRL_CR1_HSEEN_Msk));                        // Enable HSE
    while ((SYM_SYSCTRL->HSE & SYSCTRL_HSE_STABLE_Msk) != SYSCTRL_HSE_STABLE_Msk)
    {
        if ((SYM_SYSCTRL->ISR & SYSCTRL_ISR_HSEFAIL_Msk) == SYSCTRL_ISR_HSEFAIL_Msk)
        {
            return HAL_ERROR;
        }
    }
    gHseFreq = FreqIn;

    return HAL_OK;
}


/*******************************************************************************
  * @brief  使能高频晶振(外部输入时钟模式)
  * @param  FreqIn   : 输入时钟信号的频率，输入频率范围：4000000 - 32000000Hz
  * @retval HAL_OK   : 起振成功
  *         HAL_ERROR: 起振失败
  *
  *         注意：HSE使能时开启了HSECCS；如需减少功耗可关闭HSECCS
  */
HAL_StatusTypeDef HAL_SysCtrl_HSE_Enable_ExClk(uint32_t FreqIn)
{
    if ((FreqIn < 4000000UL) || (FreqIn > 32000000UL))
    {
        while (1)
        {
            ;                                                         // Error Freq
        }
    }

    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOA_Msk)); // Open GPIOA Clk
    REGBITS_SET(SYM_GPIOA->DIR, GPIOx_DIR_PIN0_Msk);                  // PA00 input
    REGBITS_SET(SYM_GPIOA->PUR, GPIOx_PUR_PIN0_Msk);                  // PA00 pull up
    REGBITS_CLR(SYM_GPIOA->ANALOG, GPIOx_ANALOG_PIN0_Msk);            // PA00 as input clk pin
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;
    uint32_t DetCnt = (8000000UL / (FreqIn / 1000UL)) << SYSCTRL_HSE_DETCNT_Pos;
    SYM_SYSCTRL->ICR = ~SYSCTRL_ICR_HSEFAIL_Msk;                      // Clear HSE Fail Flag
    SYM_SYSCTRL->HSE = DetCnt |
                       (uint32_t)SYSCTRL_HSE_MODE_CLK |
                       (uint32_t)SYSCTRL_HSE_WAITCYCLE_262144;
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY         |
                                   SYSCTRL_CR1_HSECCS_Msk |           // Enable HSECCS
                                   SYSCTRL_CR1_HSEEN_Msk));           // Enable HSE
    while ((SYM_SYSCTRL->HSE & SYSCTRL_HSE_STABLE_Msk) != SYSCTRL_HSE_STABLE_Msk)
    {
        if ((SYM_SYSCTRL->ISR & SYSCTRL_ISR_HSEFAIL_Msk) == SYSCTRL_ISR_HSEFAIL_Msk)
        {
            return HAL_ERROR;
        }
    }
    gHseFreq = FreqIn;

    return HAL_OK;
}


/*******************************************************************************
  * @brief  禁止高频晶振
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_HSE_Disable(void)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR1, (SYSCTRL_CR1_HSEEN_Msk | SYSCTRL_CR1_HSECCS_Msk), SYSCTRL_CR_KEY);
    if ((SYM_SYSCTRL->CR1 & SYSCTRL_CR1_HSEEN_Msk) == 0UL)
    {
        gHseFreq = 0UL;
    }
}


/*******************************************************************************
  * @brief  使能外部高频晶体振荡器监测
  * @param  None
  * @retval None
  * @note   None
  */
void HAL_SysCtrl_HSECCS_Enable(void)
{
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY | SYSCTRL_CR1_HSECCS_Msk));
}


/*******************************************************************************
  * @brief  禁止外部高频晶体振荡器检测
  * @param  None
  * @retval None
  * @note
  */
void HAL_SysCtrl_HSECCS_Disable(void)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR1, SYSCTRL_CR1_HSECCS_Msk, SYSCTRL_CR_KEY);
}


/*******************************************************************************
  * @brief  使能低频晶振(晶体振荡模式)
  * @param  Driver   : 驱动能力
  *                    @arg SYSCTRL_LSE_DRIVER_0
  *                    @arg SYSCTRL_LSE_DRIVER_1
  *                    @arg SYSCTRL_LSE_DRIVER_2
  *                    @arg SYSCTRL_LSE_DRIVER_3
  *                    @arg SYSCTRL_LSE_DRIVER_4
  *                    @arg SYSCTRL_LSE_DRIVER_5
  *                    @arg SYSCTRL_LSE_DRIVER_6
  *                    @arg SYSCTRL_LSE_DRIVER_7
  *                    @arg SYSCTRL_LSE_DRIVER_8
  *                    @arg SYSCTRL_LSE_DRIVER_9
  *                    @arg SYSCTRL_LSE_DRIVER_10
  *                    @arg SYSCTRL_LSE_DRIVER_11
  *                    @arg SYSCTRL_LSE_DRIVER_12
  *                    @arg SYSCTRL_LSE_DRIVER_13
  *                    @arg SYSCTRL_LSE_DRIVER_14
  *                    @arg SYSCTRL_LSE_DRIVER_15
  * @retval HAL_OK   : 起振成功
  *         HAL_ERROR: 起振失败
  * @note   注意：LSE使能时开启了LSECCS；如需减少功耗可关闭LSECCS
  */
HAL_StatusTypeDef HAL_SysCtrl_LSE_Enable_Crystal(SYSCTRL_LseDrivrTypeDef Driver)
{
    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    uint32_t PDriver   = (Driver >= SYSCTRL_LSE_DRIVER_8) ? Driver : SYSCTRL_LSE_DRIVER_8;

    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOB_Msk));     // Open GPIOB clk
    REGBITS_SET(SYM_GPIOB->ANALOG, (GPIOx_ANALOG_PIN0_Msk | GPIOx_ANALOG_PIN1_Msk)); // PB00 PB01 as analog pin
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;

    SYM_SYSCTRL->ICR = ~SYSCTRL_ICR_LSEFAIL_Msk;                      // Clear LSE Fail Flag
    SYM_SYSCTRL->LSE = (uint32_t)SYSCTRL_LSE_MODE_OSC |
                       (uint32_t)SYSCTRL_LSE_WAITCYCLE_16384 |
                       (uint32_t)(PDriver << SYSCTRL_LSE_PDRIVER_Pos) |
                       (uint32_t)Driver;
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY         |
                                   SYSCTRL_CR1_LSECCS_Msk |           // Enable LSECCS
                                   SYSCTRL_CR1_LSEEN_Msk));           // Enable LSE

    while ((SYM_SYSCTRL->LSE & SYSCTRL_LSE_STABLE_Msk) != SYSCTRL_LSE_STABLE_Msk)
    {
        if ((SYM_SYSCTRL->ISR & SYSCTRL_ISR_LSEFAIL_Msk) == SYSCTRL_ISR_LSEFAIL_Msk)
        {
            return HAL_ERROR;
        }
    }
    gLseFreq = 32768UL;

    return HAL_OK;
}


/*******************************************************************************
  * @brief  使能低频晶振(外部输入时钟模式)
  * @param  FreqIn   : 输入时钟信号的频率，输入频率范围：10000 - 100000Hz
  * @retval HAL_OK   : 起振成功
  *         HAL_ERROR: 起振失败
  *
  * @note   注意：LSE使能时开启了LSECCS；如需减少功耗可关闭LSECCS
  */
HAL_StatusTypeDef HAL_SysCtrl_LSE_Enable_ExClk(uint32_t FreqIn)
{
    if ((FreqIn < 10000UL) || (FreqIn > 100000UL))
    {
        while (1)
        {
            ;                                                         // Error Freq
        }
    }

    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOB_Msk)); // Open GPIOB clk
    REGBITS_SET(SYM_GPIOB->DIR, GPIOx_DIR_PIN1_Msk);                  // PB01 input
    REGBITS_SET(SYM_GPIOB->PUR, GPIOx_PUR_PIN1_Msk);                  // PB01 pull up
    REGBITS_CLR(SYM_GPIOB->ANALOG, GPIOx_ANALOG_PIN1_Msk);            // PB01 as input clk pin
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;

    SYM_SYSCTRL->ICR = ~SYSCTRL_ICR_LSEFAIL_Msk;                      // Clear LSE Fail Flag
    SYM_SYSCTRL->LSE = (uint32_t)SYSCTRL_LSE_MODE_CLK |
                       (uint32_t)SYSCTRL_LSE_WAITCYCLE_16384;
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY         |
                                   SYSCTRL_CR1_LSECCS_Msk |           // Enable LSECCS
                                   SYSCTRL_CR1_LSEEN_Msk));           // Enable LSE

    while ((SYM_SYSCTRL->LSE & SYSCTRL_LSE_STABLE_Msk) != SYSCTRL_LSE_STABLE_Msk)
    {
        if ((SYM_SYSCTRL->ISR & SYSCTRL_ISR_LSEFAIL_Msk) == SYSCTRL_ISR_LSEFAIL_Msk)
        {
            return HAL_ERROR;
        }
    }
    gLseFreq = FreqIn;

    return HAL_OK;
}


/*******************************************************************************
  * @brief  禁止低频晶振
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_LSE_Disable(void)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR1, (SYSCTRL_CR1_LSEEN_Msk | SYSCTRL_CR1_LSECCS_Msk), SYSCTRL_CR_KEY);
    if ((SYM_SYSCTRL->CR1 & SYSCTRL_CR1_LSEEN_Msk) == 0UL)
    {
        gLseFreq = 0UL;
    }
}


/*******************************************************************************
  * @brief  锁定低频晶振，锁定后低频晶振不可以被禁止
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_LSE_Lock(void)
{
    SYM_SYSCTRL->CR1 = SYSCTRL_CR_KEY | SYM_SYSCTRL->CR1 | SYSCTRL_CR1_LSELOCK_Msk;
}


/*******************************************************************************
  * @brief  锁定低频晶振的管脚为模拟状态，锁定后低频晶振的管脚为模拟状态不可修改
  * @param  None
  * @retval None
  * @note   1. 仅当 SYSCTRL_CR1.LSELOCK=1 时 SYSCTRL_LSE.PINLOCK=1 功能才可生效
  *         2. SYSCTRL_LSE.PINLOCK 置1后不可软件复位，仅上电复位可以复位该位域
  *         3. 当产生LSEFAIL或LSEFAULT标志后该功能自动失效，即此时LSEEN可置位可清零，
  *            直到用户软件清除上述标志
  */
void HAL_SysCtrl_LSE_PinLock(void)
{
    SYM_SYSCTRL->LSE |= SYSCTRL_LSE_PINLOCK_Msk;
}


/*******************************************************************************
  * @brief  使能外部低频晶体振荡器监测
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_LSECCS_Enable(void)
{
    REGBITS_SET(SYM_SYSCTRL->CR1, (SYSCTRL_CR_KEY | SYSCTRL_CR1_LSECCS_Msk));
}


/*******************************************************************************
  * @brief  禁止外部低频晶体振荡器检测
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_LSECCS_Disable(void)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR1, SYSCTRL_CR1_LSECCS_Msk, SYSCTRL_CR_KEY);
}


/*******************************************************************************
  * @brief  获取当前 HSI 频率
  * @param  None
  * @retval HSI频率
  *
  */
uint32_t HAL_SysCtrl_GetHsiFreq(void)
{
    uint32_t trimValue = (uint32_t)TRIMCODE_HSI_VALUE;
    uint32_t HsiDiv = SYM_SYSCTRL->HSI & SYSCTRL_HSI_DIV_Msk;

    if (((SYM_SYSCTRL->HSI & SYSCTRL_HSI_TRIM_Msk) - trimValue) > 3)  // 检查HSI的TRIM值是否正确
    {
        while (1) { ; }
        // 寄存器中的 Trim 值与 Flash 中的 Trim 值有较大偏差，本函数无法获取当前 HSI 频率
        // 建议使用 HAL_SysCtrl_ClkOut_MCO_PA04 函数通过示波器上获取当前 HSI 频率
    }

    HsiDiv = (HsiDiv == 0UL) ? 16UL : (HsiDiv >> SYSCTRL_HSI_DIV_Pos);

    return (48000000UL / HsiDiv);
}


/*******************************************************************************
  * @brief  获取当前 LSI 频率
  * @param  None
  * @retval LSI频率
  *
  */
uint32_t HAL_SysCtrl_GetLsiFreq(void)
{
    uint16_t TrimCodeInFlash = TRIMCODE_LSI_VALUE;
    uint16_t TrimCodeInReg   = SYM_SYSCTRL->LSI & SYSCTRL_LSI_TRIM_Msk;
    while (TrimCodeInReg != TrimCodeInFlash)
    {
        ;   // 寄存器中的 Trim 值与 Flash 中的 Trim 值不相等，本函数无法获取当前 LSI 频率
            // 建议使用 HAL_SysCtrl_ClkOut_MCO_PA04 函数通过示波器上获取当前 LSI 频率
    }

    return 32768UL;
}


/*******************************************************************************
  * @brief  获取当前 HSE 频率
  * @param  None
  * @retval 0, 表示HSE还没有初始化; 非0, 表示HSE的频率
  *
  */
uint32_t HAL_SysCtrl_GetHseFreq(void)
{
    return gHseFreq;
}


/*******************************************************************************
  * @brief  获取当前 LSE 频率
  * @param  None
  * @retval 0, 表示LSE还没有初始化; 非0, 表示LSE的频率
  *
  */
uint32_t HAL_SysCtrl_GetLseFreq(void)
{
    return gLseFreq;
}


/*******************************************************************************
  * @brief  获取当前 HCLK 频率
  * @param  None
  * @retval HCLK频率
  * @note   通过 HAL_SysCtrl_ClkOut_MCO_PA04 函数可在示波器上获取HCLK 频率
  *
  */
uint32_t HAL_SysCtrl_GetHclkFreq(void)
{
    uint32_t SysClkFreq = 0x0UL;

    switch (SYM_SYSCTRL->CR0 & SYSCTRL_CR0_SYSCLK_Msk)
    {
        case 0x0UL: SysClkFreq = HAL_SysCtrl_GetHsiFreq(); break;
        case 0x1UL: SysClkFreq = HAL_SysCtrl_GetHseFreq(); break;
        case 0x3UL: SysClkFreq = HAL_SysCtrl_GetLsiFreq(); break;
        case 0x4UL: SysClkFreq = HAL_SysCtrl_GetLseFreq(); break;
        default: break;
    }

    uint8_t HclkPrsIdx = (SYM_SYSCTRL->CR0 & SYSCTRL_CR0_HCLKPRS_Msk) >> SYSCTRL_CR0_HCLKPRS_Pos;
    uint8_t HclkPrs    = 0x1UL << HclkPrsIdx;

    return SysClkFreq / HclkPrs;
}


/*******************************************************************************
  * @brief  获取当前 PCLK 频率
  * @param  None
  * @retval PCLK频率
  * @note   通过 HAL_SysCtrl_ClkOut_MCO_PA04 函数在示波器上获取PCLK 频率
  *
  */
uint32_t HAL_SysCtrl_GetPclkFreq(void)
{
    uint32_t HclkFreq   = HAL_SysCtrl_GetHclkFreq();
    uint8_t  PclkPrsIdx = (SYM_SYSCTRL->CR0 & SYSCTRL_CR0_PCLKPRS_Msk) >> SYSCTRL_CR0_PCLKPRS_Pos;
    uint8_t  PclkPrs    = 0x1UL << PclkPrsIdx;

    return (HclkFreq / PclkPrs);
}


/*******************************************************************************
  * @brief  将RESET(PB07)管脚功能设置为GPIO
  * @param  None
  * @retval None
  * @note   1.RESET(PB07)管脚被设置为GPIO后，将不能通过RESET(PB07)管脚进行芯片硬件复位。
  *         2.RESET(PB07)管脚被设置为GPIO后，仅能被配置为数字输入+上拉模式，输出无效。
  */
void HAL_SysCtrl_RESET2GPIO(void)
{
    SYM_SYSCTRL->CR2 = SYSCTRL_CR_KEY | SYM_SYSCTRL->CR2 | SYSCTRL_CR2_RSTIO_Msk;
}


/*******************************************************************************
  * @brief  将RESET(PB07)管脚功能设置为RESET
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_GPIO2RESET(void)
{
    SYM_SYSCTRL->CR2 = SYSCTRL_CR_KEY | (SYM_SYSCTRL->CR2 & (~SYSCTRL_CR2_RSTIO_Msk));
}


/*******************************************************************************
  * @brief  将SWDIO(PA07)/SWCLK(PA08)管脚功能设置为GPIO
  * @param  None
  * @retval None
  * @note   SWDIO(PA07)/SWCLK(PA08)被设置为GPIO后，将不能通过SWD进行仿真及编程。
  */
void HAL_SysCtrl_SWD2GPIO(void)
{
    SYM_SYSCTRL->CR2 = SYSCTRL_CR_KEY | SYM_SYSCTRL->CR2 | SYSCTRL_CR2_SWDIO_Msk;
}


/*******************************************************************************
  * @brief  将SWDIO(PA07)/SWCLK(PA08)管脚功能设置为SWD
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_GPIO2SWD(void)
{
    SYM_SYSCTRL->CR2 = SYSCTRL_CR_KEY | (SYM_SYSCTRL->CR2 & (~SYSCTRL_CR2_SWDIO_Msk));
}


/*******************************************************************************
  * @brief  从 PA02 输出 PCLK 信号
  * @param  None
  * @retval None
  */
void HAL_SysCtrl_ClkOut_PCLK_PA02(void)
{
    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOA_Msk));
    REGBITS_MODIFY(SYM_GPIOA->AFRL, GPIOx_AFRL_AFR2_Msk, (0x1UL << GPIOx_AFRL_AFR2_Pos));
    REGBITS_CLR(SYM_GPIOA->ANALOG, GPIOx_ANALOG_PIN2_Msk);
    REGBITS_CLR(SYM_GPIOA->DIR, GPIOx_DIR_PIN2_Msk);
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;
}


/*******************************************************************************
  * @brief  从PA04 输出 MCO 信号
  * @param  Source: 时钟源
  *                 @arg SYSCTRL_MCO_SRC_NONE
  *                 @arg SYSCTRL_MCO_SRC_HCLK
  *                 @arg SYSCTRL_MCO_SRC_PCLK
  *                 @arg SYSCTRL_MCO_SRC_HSIOSC
  *                 @arg SYSCTRL_MCO_SRC_LSI
  *                 @arg SYSCTRL_MCO_SRC_HSE
  *                 @arg SYSCTRL_MCO_SRC_LSE
  * @param  Div   : 分频系数
  *                 @arg SYSCTRL_MCO_DIV1
  *                 @arg SYSCTRL_MCO_DIV2
  *                 @arg SYSCTRL_MCO_DIV8
  *                 @arg SYSCTRL_MCO_DIV64
  *                 @arg SYSCTRL_MCO_DIV128
  *                 @arg SYSCTRL_MCO_DIV256
  *                 @arg SYSCTRL_MCO_DIV512
  *                 @arg SYSCTRL_MCO_DIV1024
  * @retval None
  */
void HAL_SysCtrl_ClkOut_MCO_PA04(SYSCTRL_McoSrcTypeDef Source, SYSCTRL_McoDivTypeDef Div)
{
    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOA_Msk));
    REGBITS_MODIFY(SYM_GPIOA->AFRL, GPIOx_AFRL_AFR4_Msk, (0x4UL << GPIOx_AFRL_AFR4_Pos));
    REGBITS_CLR(SYM_GPIOA->ANALOG, GPIOx_ANALOG_PIN4_Msk);
    REGBITS_CLR(SYM_GPIOA->DIR, GPIOx_DIR_PIN4_Msk);
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;

    SYM_SYSCTRL->MCO = (uint32_t)Source | (uint32_t)Div;
}


/*******************************************************************************
  * @brief  从PA08 输出 MCO 信号
  * @param  Source: 时钟源
  *                 @arg SYSCTRL_MCO_SRC_NONE
  *                 @arg SYSCTRL_MCO_SRC_HCLK
  *                 @arg SYSCTRL_MCO_SRC_PCLK
  *                 @arg SYSCTRL_MCO_SRC_HSIOSC
  *                 @arg SYSCTRL_MCO_SRC_LSI
  *                 @arg SYSCTRL_MCO_SRC_HSE
  *                 @arg SYSCTRL_MCO_SRC_LSE
  * @param  Div   : 分频系数
  *                 @arg SYSCTRL_MCO_DIV1
  *                 @arg SYSCTRL_MCO_DIV2
  *                 @arg SYSCTRL_MCO_DIV8
  *                 @arg SYSCTRL_MCO_DIV64
  *                 @arg SYSCTRL_MCO_DIV128
  *                 @arg SYSCTRL_MCO_DIV256
  *                 @arg SYSCTRL_MCO_DIV512
  *                 @arg SYSCTRL_MCO_DIV1024
  * @retval None
  * @note   PA08默认为SWD调试端口，需要调用 HAL_SysCtrl_SWD2GPIO 函数将 PA08 释放成普通 IO 才可输出 MCO 信号
  */
void HAL_SysCtrl_ClkOut_MCO_PA08(SYSCTRL_McoSrcTypeDef Source, SYSCTRL_McoDivTypeDef Div)
{
    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOA_Msk));
    REGBITS_MODIFY(SYM_GPIOA->AFRH, GPIOx_AFRH_AFR8_Msk, (0x3UL << GPIOx_AFRH_AFR8_Pos));
    REGBITS_CLR(SYM_GPIOA->ANALOG, GPIOx_ANALOG_PIN8_Msk);
    REGBITS_CLR(SYM_GPIOA->DIR, GPIOx_DIR_PIN8_Msk);
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;

    SYM_SYSCTRL->MCO = (uint32_t)Source | (uint32_t)Div;
}


/*******************************************************************************
  * @brief  从PB03 输出 MCO 信号
  * @param  Source: 时钟源
  *                 @arg SYSCTRL_MCO_SRC_NONE
  *                 @arg SYSCTRL_MCO_SRC_HCLK
  *                 @arg SYSCTRL_MCO_SRC_PCLK
  *                 @arg SYSCTRL_MCO_SRC_HSIOSC
  *                 @arg SYSCTRL_MCO_SRC_LSI
  *                 @arg SYSCTRL_MCO_SRC_HSE
  *                 @arg SYSCTRL_MCO_SRC_LSE
  * @param  Div   : 分频系数
  *                 @arg SYSCTRL_MCO_DIV1
  *                 @arg SYSCTRL_MCO_DIV2
  *                 @arg SYSCTRL_MCO_DIV8
  *                 @arg SYSCTRL_MCO_DIV64
  *                 @arg SYSCTRL_MCO_DIV128
  *                 @arg SYSCTRL_MCO_DIV256
  *                 @arg SYSCTRL_MCO_DIV512
  *                 @arg SYSCTRL_MCO_DIV1024
  * @retval None
  */
void HAL_SysCtrl_ClkOut_MCO_PB03(SYSCTRL_McoSrcTypeDef Source, SYSCTRL_McoDivTypeDef Div)
{
    uint32_t AHBEN_Bak = SYM_SYSCTRL->AHBEN;
    REGBITS_SET(SYM_SYSCTRL->AHBEN, (SYSCTRL_CR_KEY | SYSCTRL_AHBEN_GPIOB_Msk));
    REGBITS_MODIFY(SYM_GPIOB->AFRL, GPIOx_AFRL_AFR3_Msk, (0x4UL << GPIOx_AFRL_AFR3_Pos));
    REGBITS_CLR(SYM_GPIOB->ANALOG, GPIOx_ANALOG_PIN3_Msk);
    REGBITS_CLR(SYM_GPIOB->DIR, GPIOx_DIR_PIN3_Msk);
    SYM_SYSCTRL->AHBEN = SYSCTRL_CR_KEY | AHBEN_Bak;

    SYM_SYSCTRL->MCO = (uint32_t)Source | (uint32_t)Div;
}


/*******************************************************************************
  * @brief  系统进入DeepSleep
  * @param  WakeUpClk: 系统从DeepSleep唤醒后运行的时钟
  *                    @arg WAKEUPCLK_DEFAULT：系统唤醒后SysClk来源保持进入DeepSleep之前的时钟
  *                    @arg WAKEUPCLK_HSI4MHz: 系统唤醒后SysClk来源切换为HSI4MHz
  * @retval None
  * @note   1. 进入DeepSleep之前关闭外部时钟监测可有效降低系统功耗。
  *         2. 进入深度休眠模式前应当等待FLASH_CR1.BUSY标志变为0并设置FLASH_CR1.MODE为0;
  *            SYM的FLASH驱动库已实现此注意事项, 可直接使用。
  *         3. 进入深度休眠模式前应当等待RTC_CR1.SYNC标志变为0, SYM的RTC驱动库已实现此注意事项, 可直接使用。
  */
void HAL_SysCtrl_EnterDeepSleep(SYSCTRL_WakeupClkTypeDef WakeUpClk)
{
    REGBITS_MODIFY(SYM_SYSCTRL->CR2, SYSCTRL_CR2_WAKEUPCLK_Msk, (SYSCTRL_CR_KEY | (uint32_t)WakeUpClk));
    SCB->SCR = SCB_SCR_SLEEPDEEP_Msk;
    __WFI();
}


/*******************************************************************************
  * @brief  系统进入Sleep
  * @param  None
  * @retval None
  * @note   None
  */
void HAL_SysCtrl_EnterSleep(void)
{
    SCB->SCR = 0x0UL;
    __WFI();
}


/*******************************************************************************
  * @brief  设置FLASH的读等待周期
  * @param  HclkFreq:  Hclk的频率，以Hz为单位
  * @retval None
  * @note   调整系统时钟频率时需同步调整FLASH的访问速率(SYSCTRL_CR2.FLASHWAIT等同FLASH_CR2.WAIT)
  */
void HAL_SysCtrl_ConfigWaitCycle(uint32_t HclkFreq)
{
    uint32_t tmp32;

    if (HclkFreq <= 24000000UL)
    {
        tmp32 = 0UL << SYSCTRL_CR2_FLASHWAIT_Pos;
    }
    else if (HclkFreq <= 48000000UL)
    {
        tmp32 = 1UL << SYSCTRL_CR2_FLASHWAIT_Pos;
    }
    else
    {
        while (1) { ; }
    }

    REGBITS_MODIFY(SYM_SYSCTRL->CR2,
                   SYSCTRL_CR2_FLASHWAIT_Msk,
                   SYSCTRL_CR_KEY | tmp32);
}


/*******************************************************************************
  * @brief  从FLASH中获取HSIOSC的TrimCode值并进行修正
  * @param  HsiDiv:  HSI时钟与HSIOSC时钟分频系数
  * @retval 修正后的TrimCode
  * @note
  */
static uint16_t SysCtrl_GetHSITrimCode(SYSCTRL_HsiOscDivTypeDef HsiDiv)
{
    uint16_t TrimCodeInFlash = TRIMCODE_HSI_VALUE;
    uint32_t tmpHsiDiv       = (uint32_t)HsiDiv >> SYSCTRL_HSI_DIV_Pos;

    if ((tmpHsiDiv >= 0xCUL) || (tmpHsiDiv == 0x0UL))
    {
        TrimCodeInFlash += 3U;
    }
    else if (tmpHsiDiv >= 0x6UL)
    {
        TrimCodeInFlash += 2U;
    }
    else if (tmpHsiDiv >= 0x3UL)
    {
        TrimCodeInFlash += 1U;
    }

    return TrimCodeInFlash;
}


/************************ (C) COPYRIGHT SIYIMicro *****END OF FILE*************/
